package code;
//音频工具类----------------------------------------------------------

//音频工具类。需要实例化。

import javax.sound.sampled.*;
import javax.swing.*;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;


class audioUtils {
    // 定义录音格式
    private AudioFormat af = null;
    // 定义目标数据行,可以从中读取音频数据,该 TargetDataLine 接口提供从目标数据行的缓冲区读取所捕获数据的方法。
    private TargetDataLine td = null;
    // 定义源数据行,源数据行是可以写入数据的数据行。它充当其混频器的源。应用程序将音频字节写入源数据行，这样可处理字节缓冲并将它们传递给混频器。
    private final SourceDataLine sd = null;
    // 定义字节数组输入输出流
    private ByteArrayInputStream bais = null;
    private ByteArrayOutputStream baos = null;
    // 定义音频输入流
    private AudioInputStream ais = null;
    // 定义停止录音的标志，来控制录音线程的运行
    private Boolean stopflag = false;
    private Socket s = null;

    //默认的AudioFormat
    private AudioFormat.Encoding encoding = AudioFormat.Encoding.
            PCM_SIGNED;
    private float rate = 16000f;
    private int sampleSize = 16;
    private String signedString = "signed";
    private boolean bigEndian = true;
    private int channels = 2;


    /*接收文件
     * @param socket（套接字），file文件
     *
     */
    public void recied(Socket s, File file) {

        //开启一个接收文件的线程
        recied r = new recied(s, file);
        Thread t1 = new Thread(r);
        t1.start();

    }


    /*停止录音
     *
     */
    public void stop() {
        stopflag = true;
        //之前的源音频数据流要关了
        if (td != null) {
            td.close();
        }
    }

    /*
     * 播放语音（没流默认播放当前录音流)
     */
    public void play() {
        Thread t1 = new Thread(new play(ais));
        t1.start();
    }

    /*
     * 有流播放传入的流
     * @param ais(音频输入流)
     */
    public void play(AudioInputStream ais) {
        Thread t1 = new Thread(new play(ais));
        t1.start();
    }

    /*
     * 保存文件
     * @param file(本地文件,若文件不存在会自动创建)
     */
    public void save(File file) {
        // 取得录音输入流
        af = getAudioFormat();
        byte[] audioData = baos.toByteArray();
        bais = new ByteArrayInputStream(audioData);
        ais = new AudioInputStream(bais, af, audioData.length
                / af.getFrameSize());

        // 写入文件
        try {

            if (!file.exists()) {// 如果文件不存在，则创建该文件
                file.createNewFile();
            }


            AudioSystem.write(ais, AudioFileFormat.Type.WAVE, file);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            try {
                if (bais != null) {
                    bais.close();
                }
                if (ais != null) {
                    ais.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /*
     * 开始录音
     */
    public void capture()        //录音代码
    {
        try {
            af = getAudioFormat();
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, af);
            td = (TargetDataLine) (AudioSystem.getLine(info));

            td.open(af);
            td.start();
            Record record = new Record();
            Thread t1 = new Thread(record);
            t1.start();

        } catch (LineUnavailableException ex) {
            ex.printStackTrace();
            return;
        }
    }

    /*
     * 发送录音(需要在连接后使用,否则会抛RuntimeException异常)
     * throws
     */
    public void send() {
        Thread t1 = new Thread(new Send());
        t1.start();
        if (s.isClosed()) {
            throw new RuntimeException();
        }
    }

    // 设置AudioFormat的参数
    public AudioFormat getAudioFormat()    //录音格式
    {

        return new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8) * channels, rate, bigEndian);
    }

    /*
     * 设置AduioFomat
     */
    public void setAudioFormat
    (AudioFormat.Encoding encoding, float rate, int sampleSize, String signedString, boolean bigEndian, int channels) {
        this.encoding = encoding;
        this.rate = 16000f;
        this.sampleSize = 16;
        this.signedString = "signed";
        this.bigEndian = true;
        this.channels = 2;
    }

    // 录音类，因为要用到MyRecord类中的变量，所以将其做成内部类
    class Record implements Runnable    // 录音线程
    {
        byte[] bts = new byte[10000];

        public void run() {
            baos = new ByteArrayOutputStream();
            try {
                System.out.println("客户端正在录音...");
                stopflag = false;
                while (stopflag != true) {
                    int cnt = td.read(bts, 0, bts.length);
                    if (cnt > 0) {
                        baos.write(bts, 0, cnt);
                    }
                }
                af = getAudioFormat();
                byte[] audioData = baos.toByteArray();
                bais = new ByteArrayInputStream(audioData);
                ais = new AudioInputStream(bais, af, audioData.length
                        / af.getFrameSize());
                //查看数据
                System.out.println("长度:" + ais.getFrameLength() + "最大" + ais.available());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (baos != null) {
                        baos.close();
                    }
                    if (bais != null) {
                        bais.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {

                    td.drain();
                    td.close();
                }
            }
        }

    }

    //播放线程类
    class play implements Runnable {
        private final AudioInputStream ais;

        public play(AudioInputStream ais) {
            this.ais = ais;
        }

        @Override
        public void run() {
            // TODO 自动生成的方法存根

            playAudio(ais);

        }

        //接收一个音频流
        public void playAudio(AudioInputStream ais) {
            try {
                AudioFormat baseFormat = ais.getFormat();

                DataLine.Info info = new DataLine.Info(SourceDataLine.class, baseFormat);

                SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
                System.out.println("正在播放录音");
                line.open(baseFormat);
                line.start();
                int BUFFER_SIZE = 4000 * 4;
                int intBytes = 0;
                int outBytes;
                byte[] audioData = new byte[BUFFER_SIZE];
                while (intBytes != -1) {
                    intBytes = ais.read(audioData, 0, BUFFER_SIZE);
                    if (intBytes >= 0)
                        outBytes = line.write(audioData, 0, intBytes);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    ais.close();
                } catch (IOException e) {
                    // TODO 自动生成的 catch 块
                    System.out.println("关流失败");
                }
            }
        }

    }

    //发送文件的线程类（不用保存到本地文件直接发送)
    class Send implements Runnable {

        public void run() {
            send();

        }

        private void send() {
            try {

                af = getAudioFormat();

                byte[] audioData = baos.toByteArray();

                bais = new ByteArrayInputStream(audioData);

                ais = new AudioInputStream(bais, af, audioData.length
                        / af.getFrameSize());

                if (s != null) {
                    //将输入流写入服务端的输出流
                    s.getOutputStream().flush();
                    AudioSystem.write(ais, AudioFileFormat.Type.WAVE, s.getOutputStream());
                }

                System.out.println("发送到服务端");
                ais.close();
            } catch (UnknownHostException e) {
                // TODO 自动生成的 catch 块

                e.printStackTrace();
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }
    }

    //收语音的线程，并播放的类
    class recied implements Runnable {
        InputStream in = null;
        File file = null;

        public recied(Socket socket, File file) {
            s = socket;
            this.file = file;
        }

        @Override
        public void run() {
            // TODO 自动生成的方法存根
            try {
                while (true) {
                    if (!file.exists()) {// 如果文件不存在，则创建该目录
                        file.createNewFile();
                    }

                    //获得客户端的读取流(阻塞)
                    in = s.getInputStream();

                    //这个网络流不允许读写头来回移动,也就不允许mark/reset机制需要buffer包起来。
                    BufferedInputStream bufin = new BufferedInputStream(in);

                    //获取到客户端传过来的输入流，并转化为录音输入流
                    ais = AudioSystem.getAudioInputStream(bufin);
                    //写入本地文件.
                    AudioSystem.write(ais, AudioFileFormat.Type.WAVE, file);

                    System.out.println("收到录音..");
                    JOptionPane.showMessageDialog(null, "收到录音....");
                    AudioInputStream aui = AudioSystem.getAudioInputStream(file);


                    //播放收到的声音
                    play(aui);

                }
            } catch (Exception e) {
                e.getStackTrace();
            }

        }

    }
}